package edu.upenn.seas.peerreview.smui
import edu.upenn.seas.peerreview.{Id, Sink}

/**
 * The base application ("UI") component. It sends and receives messages from
 * the state machine (but could also send/receive to other machines in the game.
 */
class RPCDecisionAdapter(smId: Id, dec : Decider, myId : Id, transport : Sink.T) 
  extends RPCAgent[BSUIInterface](myId, transport) {

  def debugGui = Debug.debug("rpc") _
  
  /** Return my Id. */
  def getId() = myId
 
  var opponents : List[Id] = List()
  var liveOpponents : List[Id] = List()
  var boardSize : Point = Point(0, 0)
  var shipSet : List[Int] = List()
  val boards : scala.collection.mutable.Map[Id, Board] = scala.collection.mutable.Map[Id,Board]()
  
  /** Return this player's board. */
  private def myBoard() : Board             = boards(smId)
  
  /** Return an opponent player's board. */
  private def getBoard(person : Id) : Board = boards(person)
  
  /** Internally update the game boards' states. */
  private def updateBoards(opp:Id, locOnBoard:Point, result:FireResultType) { 
    def toCellStatus(f:FireResultType) : CellStatus = f match {
        case Hit | Sunk | Defeat | RedundantHit => CellStatus.Hit
        case x => CellStatus.Miss
    }
    boards(opp) = boards(opp) + (locOnBoard, toCellStatus(result))
  }

  def main {  

    forkhandler('requestAction) { x => dec.makeMove(liveOpponents) } // (id, point)
    forkhandler('sendFireResult) {
      case (attacker: Id) :: (target: Id) :: (coord: Point) :: (result: FireResultType) :: Nil => {
        updateBoards(target, coord, result)
        if (attacker == smId) // Report of your firing.
          dec.onYourFireReport(target, coord, result)
        else if (target == smId) // Report of a fire at you.
          dec.onFiredAtReport(attacker, coord, result)
        else // You are not involved in this fire result.
          dec.onOpponentFireReport(attacker, target, coord, result)
        if (result == Defeat)
          liveOpponents -= target // not yet?
      }
      case _ => // Compiler warning.
    }

    forkhandler('sendPlayerResult) {
      case (player: Id) :: (hits: Int) :: (sunks: Int) :: (kills: Int) :: (isWinner: Boolean) :: Nil => {
        if (player == smId)  dec.onYourFinish(hits, sunks, kills, isWinner)
        else                 dec.onPlayerResult(player, hits, sunks, kills, isWinner)
      }
      case _ => // Compiler warning.
    }

    forkhandler('tournamentOver) { x => dec.onTournamentOver() }

    //initialize
    val sm = new RPCProxy(smId) with BSSMInterface
    sm.linkUI
    dec.onRegister(myBoard, getBoard)

    //main loop
    while (true) {
      
      respondToCall {
        case (_, 'initialize, data) =>
        data match {
          case NewGameConfigs(opp, board, ships) :: Nil => {
                  opponents = opp
                  liveOpponents = opponents
                  boardSize = board
                  shipSet = ships
                  (smId :: opp).foreach(o => boards += (o->new BoardImpl(boardSize.x, boardSize.y)))
                  dec.onNewGame(opponents, boardSize)
          }
          case _ => None
        }
        case _ => // Compiler warning.
      }

      val ships = dec.placeShips(boardSize, shipSet)
      ships.foreach {
        tup => {
          val v = Vessel((tup._1.x +tup._1.y - tup._2.x - tup._2.y).abs + 1) // Calculate size of ship
          for(x <- tup._1.x to tup._2.x; y <- tup._1.y to tup._2.y)
            boards(smId) = boards(smId) + (Point(x,y), CellStatus.Ship(v))
        }
      }

      respondToCall { case (_, 'placeShips, _) => ships }
      //game is handled through methods defined outside of loWop
      respondToCall { case (_, 'gameOver, _) => dec.onGameOver() }
    }
    debugGui("End of decision adapter main.")
  }

  /** Handle an incoming message. */
 /* def xhandle (e: Envelope) : Unit = { 
    e.contents match {
      case Forward(from, msg) => msg match {
        case MoveMade() => dec.onOpponentMoved(from)
        case FireAt(point, target) => dec.onBeingFiredAt(from, point)
      }
      case PlayerWithdrawal(removed) => dec.onPlayerWithdrawal(removed)
      case _ => 
    }
  }*/

}
